import os
import re
from typing import List, Dict, Optional
import math
from collections import defaultdict

# 模拟 Java 中工具类，实际使用需根据真实逻辑实现或替换为对应 Python 库
class LgFileUtil:
    @staticmethod
    def writeString2SimpleFile(content: str, filePath: str, encoding: str = "utf-8"):
        """模拟将字符串写入文件"""
        try:
            with open(filePath, "w", encoding=encoding) as f:
                f.write(content)
        except Exception as e:
            print(f"写入文件失败: {e}")

    @staticmethod
    def readFiles(dirPath: str) -> Dict[str, List[float]]:
        """模拟读取目录下文件内容，解析为类似 Java 中 Map<String, Double[]> 的结构，这里简化返回"""
        result = defaultdict(list)
        for root, dirs, files in os.walk(dirPath):
            for file in files:
                fileFullPath = os.path.join(root, file)
                try:
                    with open(fileFullPath, "r", encoding="utf-8") as f:
                        content = f.readlines()
                        # 简单模拟解析，实际需按真实数据格式处理
                        volumes = [float(line.strip()) for line in content if line.strip()]
                        result[file] = volumes
                except Exception as e:
                    print(f"读取文件 {fileFullPath} 失败: {e}")
        return result

# 模拟下载文件工具类，实际需结合 HDFS 操作库（如 pyhdfs 等）实现
class DownloadFile:
    @staticmethod
    def init(hdfsPath: str):
        """模拟初始化 HDFS 连接"""
        print(f"初始化 HDFS 连接到 {hdfsPath}")

    @staticmethod
    def download(hdfsDir: str, localDir: str):
        """模拟从 HDFS 下载目录"""
        print(f"从 HDFS 目录 {hdfsDir} 下载到本地目录 {localDir}")

    @staticmethod
    def destroy():
        """模拟销毁 HDFS 连接"""
        print("销毁 HDFS 连接")

# 模拟上传文件工具类，实际需结合 HDFS 操作库（如 pyhdfs 等）实现
class UploadFile:
    @staticmethod
    def init(hdfsPath: str):
        """模拟初始化 HDFS 连接"""
        print(f"初始化 HDFS 连接到 {hdfsPath} 用于上传")

    @staticmethod
    def uploadFolder(localDir: str, hdfsDir: str):
        """模拟上传本地目录到 HDFS"""
        print(f"将本地目录 {localDir} 上传到 HDFS 目录 {hdfsDir}")

    @staticmethod
    def destroy():
        """模拟销毁 HDFS 连接"""
        print("销毁上传用的 HDFS 连接")

class TransportGoods3:
    @staticmethod
    def getMaxVolumeMulti(volumeArr: List[int], capacity: int, fileName: str):
        num = 0
        # 判断货物体积数组中是否还有数据（这里用是否还有非零元素判断，类似 Java 中数组长度是否为 0 的逻辑）
        while any(x > 0 for x in volumeArr):
            num += 1
            arr = TransportGoods3.getMaxVolume(volumeArr, capacity, num, fileName)
            # 更新 volumeArr，过滤掉已选中的（置为 0 的元素后续会处理）
            volumeArr = arr

    @staticmethod
    def translate(volumeArr: List[float]) -> List[int]:
        """
        对货物体积数据进行整数化处理（转为立方分米，保留三位小数后再转为整数）
        """
        arr = []
        for v in volumeArr:
            # 保留三位小数并转为整数（立方分米）
            rounded = round(v * 1000, 3)
            arr.append(int(rounded))
        return arr

    @staticmethod
    def getMaxVolume(volumeArr: List[int], capacity: int, num: int, fileName: str) -> List[int]:
        """
        类似 01 背包求解，返回更新后的体积数组（已选中的置为 0 等处理）
        """
        n = len(volumeArr)
        # 定义状态数组
        dp = [[0] * (capacity + 1) for _ in range(n + 1)]
        # 遍历数据
        for i in range(1, n + 1):
            for j in range(1, capacity + 1):
                if j < volumeArr[i - 1]:
                    # 剩余容积不满足选择该物品
                    dp[i][j] = dp[i - 1][j]
                else:
                    # 选或者不选
                    dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - volumeArr[i - 1]] + volumeArr[i - 1])

        print(f"\n---------------------")
        print(f"第 {num} 辆车, 最大组合体积为 {dp[n][capacity]}")
        # 输出结果到目标文件
        resultStr = f"第 {num} 辆车, 最大组合体积为 {dp[n][capacity]}"
        LgFileUtil.writeString2SimpleFile(resultStr, fileName, "utf-8")
        # 调用详细货物组合方案信息
        newArr = TransportGoods3.printGoodsInfo(dp, volumeArr, capacity, fileName)
        return newArr

    @staticmethod
    def printGoodsInfo(dp: List[List[int]], volumeArr: List[int], capacity: int, fileName: str) -> List[int]:
        """
        打印货物组合信息，并返回更新后的体积数组（选中的置为 0 ，后续过滤）
        """
        i = len(volumeArr)
        j = capacity
        newArr = volumeArr.copy()  # 复制数组，用于修改
        while i > 0 and j > 0:
            try:
                if dp[i][j] == dp[i - 1][j - newArr[i - 1]] + newArr[i - 1]:
                    # 说明选中了 newArr[i - 1] 这个货物
                    print(f"选择了体积为 {newArr[i - 1]} 货物。")
                    LgFileUtil.writeString2SimpleFile(f"{newArr[i - 1]}", fileName, "utf-8")
                    j -= newArr[i - 1]
                    # 标记已选择的物品（置为 0）
                    newArr[i - 1] = 0
            except IndexError:
                pass
            # 不管是否选择了物品，i 都要移动到上一行
            i -= 1
        # 过滤掉为 0 的元素（生成新数组，类似 Java 中过滤掉 0 数据）
        return [x for x in newArr if x > 0]

    @staticmethod
    def getNewArr(volumeArr: List[int]) -> List[int]:
        """剔除选择的物品（过滤掉为 0 的元素）"""
        return [x for x in volumeArr if x > 0]

if __name__ == "__main__":
    # hdfs 和本地相关目录信息
    day = "20200608"
    hdfsDir = "/replenishment"
    hdfsResDir = "/res"

    localDir = "e:/replenishment"
    localResDir = "e:/res"
    capacity = 40 * 1000  # 容量，单位立方分米

    # 下载 hdfs 的数据目录（模拟，实际需结合 HDFS 操作）
    try:
        DownloadFile.init("hdfs://lgns")
        DownloadFile.download(f"{hdfsDir}/{day}", f"{localDir}/{day}")
        DownloadFile.destroy()
    except Exception as e:
        print(f"下载 HDFS 数据失败: {e}")

    # 读取下载好的本地的数据文件，解析数据得到物体积数组，然后调用动态规划算法
    mapData: Optional[Dict[str, List[float]]] = None
    try:
        mapData = LgFileUtil.readFiles(f"{localDir}/{day}")
    except Exception as e:
        print(f"读取本地文件失败: {e}")

    if not mapData:
        print("未获取到有效数据，程序退出")
        exit(0)

    # 遍历 map 获取每个仓库数据并处理
    for key, volumeArr in mapData.items():
        # 转换为整数数组（立方分米）
        translatedArr = TransportGoods3.translate(volumeArr)
        # 构建输出文件名，这里简单处理
        outputFileName = f"{localResDir}/{day}/{key}_result.txt"
        TransportGoods3.getMaxVolumeMulti(translatedArr, capacity, outputFileName)

    # 上传结果文件到 hdfs（模拟，实际需结合 HDFS 操作）
    try:
        UploadFile.init("hdfs://lgns")
        UploadFile.uploadFolder(f"{localResDir}/{day}", f"{hdfsResDir}/{day}")
        UploadFile.destroy()
    except Exception as e:
        print(f"上传结果到 HDFS 失败: {e}")

    # 保存调度信息到 mysql（车辆调度信息）以及 hbase（装载货物信息）等，这里仅示例，需结合实际数据库操作
    try:
        # 模拟读取调度文件并处理，实际需按真实逻辑实现
        scheduleFiles = LgFileUtil.readFiles(f"{localResDir}/{day}")
        for file, content in scheduleFiles.items():
            print(f"处理调度文件 {file}，内容: {content}")
            # 这里可添加真实的数据库保存逻辑，如使用 pymysql 等操作 mysql
    except Exception as e:
        print(f"保存调度信息失败: {e}")